﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using Microsoft.Xna.Framework;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

using Vector2 = Microsoft.Xna.Framework.Vector2;
using Rectangle = System.Drawing.Rectangle;
using Color = System.Drawing.Color;
using Point = System.Drawing.Point;

namespace MaterialEditor
{
    public abstract class Node : ICloneable
    {
        public Vector2 position;

        public virtual int Width
        {
            get 
            {
                int outval = int.MinValue;
                for (int i = 0;  input != null && i < input.Length; i++)
                    outval = Math.Max(outval, (int)Fonts.fonts["Arial"].MeasureString(input[i].name).X);

                int inputWidth = Math.Max(100, outval + InputSpacing);
                int valueWidth = Result != null && Result.type == NodeValueType.ValueTexture ? NodeValue.textureRectDim : 0;
                return Math.Max(inputWidth, valueWidth);
            }
        }

        private int InputHeight
        {
            get
            {
                int outval = 50;
                if (input != null && input.Length > 2)
                    outval += (input.Length - 2) * InputSpacing;

                return outval;
            }
        }

        public virtual int Height
        {
            get { return InputHeight + (Result != null && Result.type == NodeValueType.ValueTexture ? NodeValue.textureRectDim : 0); }
        }

        public InputSlot[] input;

        public InputSlot output;

        protected Color backgroundColor = Color.RoyalBlue;

        public abstract NodeValue Result { get; }

        public abstract NodeValueType[][] acceptTypes { get; }

        /// <summary>
        /// The group this node belongs to
        /// </summary>
        public NodeGroupBox group;
        
        public virtual bool CanEdit
        {
            get { return false; }
        }

        /// <summary>
        /// Indicates that the node can be removed
        /// </summary>
        public virtual bool CanRemove
        {
            get { return true; }
        }

        public Microsoft.Xna.Framework.Vector2 outputLocation
        {
            get { return new Microsoft.Xna.Framework.Vector2(Rect.Right - (InputSpacing), (int)position.Y - 3 - (InputSpacing)); }
        }

        public Vector2 GetInputLocation(InputSlot slot)
        {
            if (input != null)
            {
                for (int i = 0; i < input.Length; i++)
                    if (input[i] == slot)
                        return new Vector2(Rect.Left - InputSpacing / 3, Rect.Top + (i * (InputSpacing + 5)) + (InputSpacing / 2.0f));
            }

            if (output != null && slot == output)
                return new Vector2(Rect.Right + InputSpacing, (int)position.Y - InputSpacing);

            return Vector2.Zero;
        }

        public Rectangle Rect
        {
            get { return new Rectangle((int)position.X - Width / 2, (int)position.Y - Height / 2, Width, Height); }
        }

        public static int InputSpacing
        {
            get { return 15; }
        }

        public bool Pickup;

        protected string Name = "UNNAMED NODE";

        public Node(int x, int y)
        {
            position = new Vector2(x, y);

            if (Form1.instance != null)
                Form1.instance.AddUndoAction(new AddNodeAction(this));
        }

        public void UnGroup()
        {
            if (group != null)
            {
                group.nodes.Remove(this);
                group = null;
            }
        }

        /// <summary>
        /// Override this to set a different position for values to be renderd
        /// </summary>
        protected virtual Point ValuePosition
        {
            get { return new Point((int)position.X, Rect.Top + InputHeight + NodeValue.textureRectDim / 2); }
        }

        /// <summary>
        /// Gets an enumeration of all node connectors connected to this node
        /// </summary>
        public IEnumerable<NodeConnector> AttachedConnectors
        {
            get
            {
                if (input != null)
                {
                    for (int i = 0; i < input.Length; i++)
                        if (input[i].connector != null)
                            yield return input[i].connector;
                }

                if (output.connector != null)
                    yield return output.connector;
            }
        }

        public virtual void Update()
        {
            if (output != null)
                output.value = Result;
        }

        public virtual void Draw()
        {
            Graphics graphics = Graphics.Instance;

            graphics.DrawRect(Rect, backgroundColor);

            Color borderColor = Pickup ? Color.FromArgb(backgroundColor.A,
                (int)MathHelper.Clamp(backgroundColor.R + 50, 0, 255),
                (int)MathHelper.Clamp(backgroundColor.G + 50, 0, 255),
                (int)MathHelper.Clamp(backgroundColor.B + 50, 0, 255)) :
                Color.FromArgb(backgroundColor.A,
                (int)MathHelper.Clamp(backgroundColor.R - 50, 0, 255),
                (int)MathHelper.Clamp(backgroundColor.G - 255, 0, 255),
                (int)MathHelper.Clamp(backgroundColor.B - 50, 0, 255));

            graphics.DrawHollowRect(Rect, borderColor);
            graphics.DrawText(new Microsoft.Xna.Framework.Vector2(Rect.X, Rect.Top - Fonts.fonts["Arial"].MeasureString(Name).Y), Fonts.fonts["Arial"], Name, Color.White);

            if (input != null)
            {
                for (int i = 0; i < input.Length; i++)
                {
                    graphics.DrawRect(input[i].Rect, input[i].color);
                    graphics.DrawHollowRect(input[i].Rect, Color.Black);
                    graphics.DrawText(new Vector2(input[i].Rect.Right, input[i].position.Y - Fonts.fonts["Arial"].MeasureString(input[i].name).Y / 3.0f), Fonts.fonts["Arial"], input[i].name, Color.White);
                }
            }

            if (output != null)
            {
                graphics.DrawRect(output.Rect, output.color);
                graphics.DrawHollowRect(output.Rect, Color.Black);
            }

            if (Result != null)
                Result.Draw(ValuePosition.X, ValuePosition.Y);
        }



        public abstract object Clone();
    }
    
    public delegate void BasicDelegate();
}
